OscRecv recv;
6449 => recv.port;
recv.listen();
recv.event( "/instrument, s s f f f s" ) @=> OscEvent oscInstrument;
recv.event( "/pause, s" ) @=> OscEvent oscPause;


140 => float bpm;
1::minute/bpm/16 => dur vierundsechzigstelTakt;
1::minute/bpm/8 => dur zweiunddreissigstelTakt;
1::minute/bpm/4 => dur sechzehntelTakt;
1::minute/bpm/2 => dur achtelTakt;
1::minute/bpm => dur viertelTakt;
1::minute/bpm*2 => dur halbTakt;
1::minute/bpm*4 => dur takt;

spork ~ pollOscInstrumentEvent();
spork ~ pollOscPauseEvent();

/*markov variables
*/

/*             note/rest 
*         whole: 1 / 21
*          half: 2 / 22
*       quarter: 4 / 24
*   quarter-dot: 6 / -
*         eigth: 8 / 28
*     sixteenth: 16 / 36
* thirty-second: 32 / -
*/
[1,2,4,6,8,16,21,22,24,28,32,36] @=> int help_arr[];


//a few lines of vivaldis spring
[8,8,8,8,16,16,6,16,16,8,8,8,16,16,6,16,16,8,16,16,8,8,8,8,8,8,
 8,8,8,16,16,6,16,16,8,8,8,16,16,6,16,16,8,16,16,8,8,4,28,8,8,16,16,8,8,8,4,8,
 8,16,16,8,8,8,4,8,8,4,8,8,16,16,4,4,28,8,8,16,16,8,8,8,4,8,8,16,16,8,8,
 16,4,8,8,4,8,8,16,16,4,4,4,4,4,4,4,8,8,8,8,8,8,8,8,
 8,8,8,8,8,8,8,16,16,16,16,16,16,16,16,16,16,22,21,
 24,28,8,8,8,8,8,6,8,8,8,8,8,4,24,4,16,16,16,16,
 4,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,8,8,4,24,4,24,4,
 24,4,6,6,2,6,6,2,24,28,8,
 8,16,16,8,8,8,4,8,8,16,16,8,8,8,4,8,8,4,8,8,16,16,4,
 8,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,8,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
 4,28,8,2,2,2,2,2,
 2,4,28,8,8,16,16,8,8,8,4,8,8,16,16,8,8,8,4,8,8,4,8,8,16,16,4,
 16,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,24,32,32,32,32,32,32,32,32,24,
 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
 4,24,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,4,28,
 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,4,24,16,16,16,16,16,16,16,16,16,16,16,16,
 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,4,28,8,
 8,16,16,8,8,8,4,8,8,16,16,8,8,8,4,8,8,4,8,8,16,16,4,
 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,1,
 1,16,16,16,16,4,16,16,16,16,4,16,16,16,16,16,16,16,16,2,
 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,2,8,8,8,16,16,6,16,16,
 8,8,8,16,16,6,16,16,8,8,8,16,16,8,8,8,16,16,8,8,8,16,16,8,8,8,16,16,
 8,8,8,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
 22,4,4,4,8,8,2,4,4,4,8,8,2,4,4,4,6,8,4,4,4,4,2,4,4,4,2,4,4,4,21,4,4,4] @=> int vivaldi_spring[];


[32,32,32,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,16,2,8,8,8,8,8,8,16,3,3,3,4,8,8,4,
8,8,4,4,2,8,8,8,8,8,8,8,8,8,8,8,8,16,2,4,2,4,4,2,2,8,8,8,8,8,8,8,8,8,4,2,8,4,2,8,4,
2,8,4,2,2,2,2,2,2,2,4,4,4,8,8,8,8,8,8,16,8,8,8,8,8,8,8,8,8,8,8,8,16,2,4,4,2,8,8,8,4,
2,4,4,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,4,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,1,8,8,8,8,8,8,
8,8,8,8,8,8,16,2,8,8,8,8,8,8,16,2,2,2,4,4,4,4,4,8,8,2,8,8,8,8,8,8,8,8,8,16,2,4,4,2,2,
2,2,2,1,8,8,8,4,2,8,8,8,4,2,8,8,8,4,2,8,8,8,4,2,2,2,3,3,3,4,4,4,4,4,2,4,4,2,2,4,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,2,8,8,8,8,8,8,8,2,8,8,8,
8,8,8,8,8,8,8,8,2,2,1,8] @=> int beethoven_mondscheinsonate[];

[32,32,32,1,1,2,2,2,2,1,16,16,1,16,16,2,2,2,4,4,4,24,22,1,2,8,8,2,4,4,2,4,4,2,2,24,24,
2,2,4,2,4,4,4,4,2,2,2,2,2,2,2,4,4,24,8,8,2,8,8,2,4,4,2,4,2,4,4,1,28,16,16,16,4,4,4,1,1,2,2,
2,2,22,1,8,8,2,2,2,2,4,4,4,24,24,8,8,2,4,4,2,4,4,1,1,2,2,1,4,4,4,4,2,2,2,4,4,4,4,4,2,4,4,2,
2,4,8,8,8,8,4,4,1,2,4,4,1,2,2,4,4,1] @=> int beethoven_mondschein2[];

int drumline[6][1];

[1,2,2,2,2] @=> drumline[3];
[2,2,4,4,2,2,2,4,8,4,2,4,4,2] @=>  drumline[2];
[2,4,4,4,2,4,8,8,8,8,2,1] @=>  drumline[1];
[2,2,4,4,2,2,4,4] @=>  drumline[4];
[1,2,2,2,1,2,2,4,2,2,1,4] @=>  drumline[5];


initMarkov(vivaldi_spring) @=> float markovSpring[][][][];
initMarkov(beethoven_mondschein2) @=> float markovTest[][][][];
initMarkov(beethoven_mondscheinsonate) @=> float markovMond[][][][];
/* markov variables
*/

// variables to be defined
string instrumentToPlay;
string weatherCondition;
float temperature;
float humidity;
float windSpeed;
string windDirection;
int wolfram[];
string instrumentToPause;

WeatherInstrument instruments[0];

new WeatherInstrument @=> instruments["drums"];
instruments["drums"].setName("drums");

new WeatherInstrument @=> instruments["gesang"];
instruments["gesang"].setName("gesang");

new WeatherInstrument @=> instruments["klavier"];
instruments["klavier"].setName("klavier");
ModalBar klavierInst1;
ModalBar klavierInst2;

new WeatherInstrument @=> instruments["shakers"];
instruments["shakers"].setName("shakers");
Shakers shakersInst1;
Shakers shakersInst2;

new WeatherInstrument @=> instruments["saxofony"];
instruments["saxofony"].setName("saxofony");
Saxofony saxofonyInst1;
Saxofony saxofonyInst2;

while(true){
    1::second => now;  
}





/* OSC Handling
 *
 */

// waits for instrument data
fun void pollOscInstrumentEvent(){
    zweiunddreissigstelTakt - (now % zweiunddreissigstelTakt) => now;
    
    while(true){
        oscInstrument => now;
        
        while( oscInstrument.nextMsg() ){
            oscInstrument.getString() => instrumentToPlay;

            oscInstrument.getString() => weatherCondition;
            oscInstrument.getFloat() => temperature;
            oscInstrument.getFloat() => humidity;
            oscInstrument.getFloat() => windSpeed;
            oscInstrument.getString() => windDirection;
                        
            setInstrumentValues(instrumentToPlay);
            //printReceivedValues();
            <<<instrumentToPlay>>>;
            
            instruments[instrumentToPlay].printValues();
            
            if(instrumentToPlay == "drums")
                instruments["drums"].pause();
            instruments[instrumentToPlay].play(); 
        }         
    }
}

// waits for pausing instruments
fun void pollOscPauseEvent(){
    zweiunddreissigstelTakt - (now % zweiunddreissigstelTakt) => now;
    
    while(true){
        oscPause => now;
        
        while( oscPause.nextMsg() ){
            oscPause.getString() => instrumentToPause;
            <<<"instrumentToPause: ",instrumentToPause>>>;
            
            instruments[instrumentToPause].pause();
        }
    }
}
/* 
 * OSC Handling
 */





/* Instruments
 *
 */

 
 
 fun void initwolfram() {
     
     
     instruments["drums"].values["temperature"] $ int => int temp;
     temp/2=>temp;
     int data[temp];

     for (0=>int i;i<temp;i++) {
         if(temp/2==i) {
             1=>data[i];
         } else {
             0=>data[i];
         }
     }
     data @=> wolfram;
     
 }
 
fun int[] convert2b(int dec_zahl){
    int bin_zahl[8];

    //convert to binary
    for(0 => int i; i < bin_zahl.cap(); i++){
        if(dec_zahl >= (Math.pow(2,7-i) $ int)){
            1 => bin_zahl[i];
            dec_zahl - (Math.pow(2,7-i) $ int) => dec_zahl;
        }
    }
    
    return bin_zahl;
}



// sets the next aray for wolfram
 fun void newwolfram(int rule) {   
     int newwolfi[wolfram.cap()];
     
     convert2b(rule) @=>int bin[];
     
     for (0=>int i;i<wolfram.cap();i++) {
         // determine border s
         int l;
         int m;
         int r;
         
         if (i==0) {
             wolfram[wolfram.cap()-1] => l;
             wolfram[i+1]=>r; 
         } else if (i==wolfram.cap()-1) {
             wolfram[i-1] => l;
             wolfram[0]=>r; 
         } else {
             wolfram[i-1] => l;
             wolfram[i+1] => r; 
         }
         wolfram[i]=>m;
         
         
             if (l==1 && m==1 && r==1) {
                 bin[0]=>newwolfi[i];
             } else if (l==1 && m==1 && r==0) {
                 bin[1]=>newwolfi[i];
             } else if (l==1 && m==0 && r==1) {
                 bin[2]=>newwolfi[i];
             } else if (l==1 && m==0 && r==0) {
                 bin[3]=>newwolfi[i];
             } else if (l==0 && m==1 && r==1) {
                 bin[4]=>newwolfi[i];
             } else if (l==0 && m==1 && r==0) {
                 bin[5]=>newwolfi[i];
             } else if (l==0 && m==0 && r==1) {
                 bin[6]=>newwolfi[i];
             } else if (l==0 && m==0 && r==0) {
                 bin[7]=>newwolfi[i];
             } 
         
     }
     newwolfi @=> wolfram;
 }


fun void playDrum() {
    instruments["drums"].setId(me.id());
    initwolfram();
    
    instruments["drums"].values["temperature"] => float temp;
    instruments["drums"].values["weatherCondition"] => float condition;
    getHumidityClass(instruments["drums"].values["humidity"] ) $ int=> int chosendrumline;
    instruments["drums"].values["windSpeed"] => float windspeed;

    drumline[chosendrumline] @=>int drumline1[];
    0=>int pos;
    time startintervall;
    dur intervallduration;
    int rule;
    
    while (true) {
        //10::samp=>dur duration;
        
        // check position in drumline
        if (pos==drumline1.cap()-1) 0=>pos; 
        
        if  (getWindSpeedClass(windspeed)==1) {
            120 => rule;
        } else if (getWindSpeedClass(windspeed)==2) {
            90 => rule;
        } else if (getWindSpeedClass(windspeed)==2) {
            30 => rule;
        } else {
            150 => rule;
        }
        
        for (0=>int i;i<wolfram.cap();i++) {
            if (wolfram[i]==1) {
                i+1 =>int filenumber; 
                
                
                // random good and bad if condition is 2
                condition => float newcondition;
                string conditiondir; 
                if (condition==2) {
                    Std.rand2(1,2)=>int rand;
                    if (rand==2){ 3=>newcondition;} 
                    else { 1=>newcondition;}
                } 
                if (newcondition==1) {
                    "good/"=>conditiondir;
                } else {
                    "bad/"=>conditiondir;
                }
                // every drumfile is played in its own spork
                spork ~ playSound(conditiondir+filenumber);
                
            }    
        }
        newwolfram(rule);
        
        pos++;
        getdurationFromInt(drumline1[pos]) * (30/(35+temp)) =>dur duration;
     
        duration=>now;
    }
}
/* plays a chuck instrument
 * @param:
 *  name: name of the instrument according to data-structure respectively inst-name from website
 *  inst1: chuck instrument
 *  inst2: second chuck instrument (just to play shorter notes with a higher pitch)
 *         should be of the same type as inst1 - otherwise it sounds strange
 *  gain: gain for the instruments (ModalBar is good hearable at ~0.9, others ar too loud at 0.9 -> set lower values for those ones.)
*/
fun void playChuckInstrument(string name, StkInstrument inst1, StkInstrument inst2, float gain) {
    instruments[name].setId(me.id());
    
    
    inst1 => dac;
    inst2 => dac;
       
    float markovToUse[][][][];
    int scaleToUse[];
    [0,2,4,5,7,9,11,12] @=> int majorC[]; //c-dur
    [-3,-1,0,2,4,5,7,9] @=> int minorA[]; //a-moll
    [-8,-6,-5,-3,-1,1,3,4] @=> int minorE[]; //e-moll
    
    0 => int lastPitch;
    int newPitch;
    0 => int sameStepsDone;
    
    while(true){
        if(instruments[name].values["weatherCondition"] == 3.0){
            markovTest @=> markovToUse;
            minorE @=> scaleToUse;
        }
        else if(instruments[name].values["weatherCondition"] == 2.0){
            markovMond @=> markovToUse;
            minorE @=> scaleToUse;
        }
        else if(instruments[name].values["weatherCondition"] == 1.0){
            markovSpring @=> markovToUse;
            majorC @=> scaleToUse;
        }
        
        getNextNote(instruments[name].lastNotes[0], instruments[name].lastNotes[1], instruments[name].lastNotes[2], markovToUse) => instruments[name].noteLength;
        saveNote(instruments[name].noteLength, instruments[name].lastNotes);
        getWindSpeedClass(instruments[name].values["windSpeed"]) => float maxPitchDifference;
        getHumidityClass(instruments[name].values["humidity"]) => float stepLimit;
        instruments[name].values["windDirection"] => float dominantDirection;
        getOctave(instruments[name].values["temperature"] $ int) => int intervall;
        
      
        random_walk(lastPitch, sameStepsDone, maxPitchDifference, stepLimit, dominantDirection) => newPitch;
        if(newPitch == lastPitch)
            sameStepsDone++;
        else
            0 => sameStepsDone;
            
        //<<< "dur: ",instruments[name].noteLength >>>;
        //<<< newPitch >>>;
        //<<< intervall >>>;
        
        if(isRest(instruments[name].noteLength) == 1){
            //<<< "rest!" >>>;
            inst1.noteOff(gain);
            inst2.noteOff(gain);
        } else{
            if(instruments[name].noteLength > 8){
                Std.mtof( intervall + scaleToUse[lastPitch]) => inst1.freq;
                inst1.noteOn(gain);
            }else{
                Std.mtof( intervall -12 + scaleToUse[lastPitch]) => inst2.freq;
                inst2.noteOn(gain);
            }
        }
        
        getDuration(instruments[name].noteLength) => now;
        
        inst1.noteOff(gain);
        inst2.noteOff(gain);
        getRestDuration(instruments[name].noteLength) => now;
        
        newPitch => lastPitch;
    }
}

fun void playGesang() {
    instruments["gesang"].setId(me.id());
    
    
    while (true) {
        instruments["gesang"].values["weatherCondition"]=>float condition;
        
        10::samp=>dur duration;
        <<<condition>>>;
        string voicedir;
        if(condition==1) {
            "voice/happy/"=> voicedir;
        } else if (condition==2) {
            "voice/neutral/"=> voicedir;
        } else {
            "voice/sad/"=> voicedir;
        }
        
        
        
        Std.rand2(1,5) => int randsample;
        Std.rand2(40,50) => int randfaktor;
        playSound(voicedir+randsample,(30+instruments["gesang"].values["temperature"])/randfaktor);
        
        zweiunddreissigstelTakt * ( Math.max(1, (30 - instruments["gesang"].values["temperature"])) ) => duration;
     
        duration=>now;
    }
}

fun void playInstrument(string name){
    zweiunddreissigstelTakt - (now % zweiunddreissigstelTakt) => now;
    
    // if instrument is currently playing
    if(instruments[name].getId() != -1)
        return;
    
    if(name == "drums")
        spork ~ playDrum();
    else if(name == "klavier")
        spork ~playChuckInstrument("klavier", klavierInst1, klavierInst2, 0.95);
    else if(name == "gesang")
         spork ~ playGesang();
    else if(name == "shakers")
         spork ~playChuckInstrument("shakers", shakersInst1, shakersInst1, 0.05);
    else if(name == "saxofony")
         spork ~playChuckInstrument("saxofony", saxofonyInst1, saxofonyInst2, 0.01);
}

class WeatherInstrument{
    string name;
    float values[0];
    -1 => int shredId;
    
    StkInstrument inst1;
    StkInstrument inst2;
    
    [32,32,32] @=> int lastNotes[]; //first 3 notes according to which markov will compute next note
    0 => int noteLength;
    
    fun void setName(string _name){
        _name => name;
    }
    
    fun string getName(){
        return name;
    }
    
    fun void setValues(float array[]){
        array @=> values;
    }
    
    fun float[] getValues(){
        return values;
    }
    
    fun void setId(int id){
        id => shredId;
    }
    
    fun int getId(){
        return shredId;
    }
    
    fun void printValues(){
        <<<"weatherCondition: ",values["weatherCondition"]>>>;
        <<<"temperature: ",values["temperature"]>>>;
        <<<"humidity: ",values["humidity"]>>>;
        <<<"windSpeed: ",values["windSpeed"]>>>;
        <<<"windDirection: ",values["windDirection"]>>>;
        <<<"------------------------">>>;
    }
    
    fun void play(){
        playInstrument(name);
    }
    
    fun void pause(){        
        if(shredId != -1){
            Machine.remove(shredId);
            -1 => shredId;
        }
        if(name == "klavier"){
            klavierInst1.noteOff(1);
            klavierInst2.noteOff(1);
        } else if(name == "shakers"){
            shakersInst1.noteOff(1);
            shakersInst2.noteOff(1);
        } else if(name == "saxofony"){
            saxofonyInst1.noteOff(1);
            saxofonyInst2.noteOff(1);
        }
    }

}
/* 
 * Instruments
 */




/* Automata and other crazy stuff
 * 
 */
        
    
    /* MARKOV CHAIN 3rd order
     * 
     * return markov-probabilities
     * @param:
     *  notes[]: int array with note-lengths
     */
    function float[][][][] initMarkov(int notes[]){

        13 => int wolfram_rule; //has to be in range(0,255)

        //markov[1st_note][2nd_note][3rd_note][probabilites for following note]
        float markov[37][37][37][37];
        int markovHelp[37][37][37][37];


        0 => int count;
        0 => int note;
        0 => int note_3;
        0 => int note_2;
        0 => int note_1;

        //count note occurrences
        for(3 => int i; i < notes.cap(); i++){
            notes[i-3] => note_3;
            notes[i-2] => note_2;
            notes[i-1] => note_1;
            notes[i] => note;
            markovHelp[note_3][note_2][note_1][note]++;
        }

        //calc note probabilities
        for(0 => int i; i < help_arr.cap(); i++){
            for(0 => int j; j < help_arr.cap(); j++){
                for(0 => int h; h < help_arr.cap(); h++){
                    calc_prob(markovHelp[help_arr[i]][help_arr[j]][help_arr[h]]) @=> markov[help_arr[i]][help_arr[j]][help_arr[h]];
                }
            }
        }
        
        return markov;
    }


    fun float[] calc_prob(int arr[]){
        //calculates note probabilities
        0 => int sum;
        for(0 => int i; i < help_arr.cap(); i++){
            arr[help_arr[i]] +=> sum;
        }
        
        float probs[37];
        sum $ float => float float_sum;
        
        if(sum > 0.0){
            for(0 => int i; i < help_arr.cap(); i++){
                (arr[help_arr[i]] / float_sum) * 100 => probs[help_arr[i]];
            }
        }
        
        return probs;
    }

    fun dur getDuration(int note){
        //markov chain: how long is the note or rest?
        if(isRest(note) == 1) 20 -=> note;
        140 => float bpm;    
        1::minute/bpm/4 => dur sechszehntel;
        
        (sechszehntel)*(32/note) => dur duration;
        (duration*0.05) -=> duration;
        return duration;
    }

    fun dur getRestDuration(int note){
        //markov chain: how long is the note or rest?
        if(isRest(note) == 1) 20 -=> note;
        140 => float bpm;
        1::minute/bpm/4 => dur sechszehntel;
        
        (sechszehntel)*(32/note) => dur duration;
        0.05 *=> duration;
        return duration;
    }

    fun int isRest(int note){
        //markov chain: boolean-function: is note rest?
        if(note > 20 && note != 32) return 1;
        else return 0;
    }

    fun int getNextNote(int x, int y, int z, float markov[][][][]){
        //markov chain: get next note to play
        markov[x][y][z] @=> float probability[];
        Math.rand2f(0.0,100.0) => float rand;

        0.0 => float sum;
        
        for(0 => int i; i < help_arr.cap(); i++){
            sum + probability[help_arr[i]] => sum;
            if(rand <= sum) return help_arr[i];
        }
    }

    /* MARKOV CHAIN
    */


    /* 1D RANDOM WALK
     * 
     * get next tone pitch
     * @param:
     *  lastPitch: last tone
     *  sameStepsDone: number of made steps without changing pitch
     *  maxPitchDifference: how big can the pitch difference be? (e.g. 3: next step = current pitch +/- 3)
     *  stepLimit: how many steps can be walked at max, staying on the same pitch?
     *  dominantDirection: in which direction should it walk? Up or down?
    */
    fun int random_walk(int lastPitch, int sameStepsDone, float maxPitchDifference, float stepLimit, float dominantDirection ){
        //<<< "sameStepsDone: "+sameStepsDone>>>;
        
        //changePitch percentages
        int changePitch_perc[];
        [100] @=> int changePitch_perc1[];
        [80,100] @=> int changePitch_perc2[];
        [60,80,100] @=> int changePitch_perc3[];
        [40,60,80,100] @=> int changePitch_perc4[];
        [20,40,60,80,100] @=> int changePitch_perc5[];
        
        if(stepLimit == 1.0)
            changePitch_perc1 @=> changePitch_perc;
        else if(stepLimit == 2.0) 
            changePitch_perc2 @=> changePitch_perc;
        else if(stepLimit == 3.0) 
            changePitch_perc3 @=> changePitch_perc;
        else if(stepLimit == 4.0) 
            changePitch_perc4 @=> changePitch_perc;
        else if(stepLimit == 5.0) 
            changePitch_perc5 @=> changePitch_perc;
            
        // change dominant direction
        float directionDelta;
        if(dominantDirection == 1.0)//N
            10.0 => directionDelta;
        else if(dominantDirection == 2.0)//NE
            3.0 => directionDelta;
        else if(dominantDirection == 3.0)//E
            2.0 => directionDelta;
        else if(dominantDirection == 4.0)//SE
            1.5 => directionDelta;
        else if(dominantDirection == 5.0)//S
            1.1 => directionDelta;
        else if(dominantDirection == 6.0)//SW
            1.5 => directionDelta;
        else if(dominantDirection == 7.0)//W
            2.0 => directionDelta;
        else if(dominantDirection == 8.0)//NW
            3.0 => directionDelta;
        
        
        int boolChange;
        float changePlus;
        float changeMinus;
        
        Math.rand2(0, 100) => int randomInt;
        
        // if sameStepsDone are greater than allowed (limit was changed)
        if( (sameStepsDone + 1) <= changePitch_perc.cap() ){
            
            if(changePitch_perc[sameStepsDone] > randomInt){
                1 => boolChange;
                changePitch_perc[sameStepsDone] => changePlus;
                changePitch_perc[sameStepsDone]/directionDelta => changeMinus;
            }
            else 0 => boolChange;
            
        } else 1 => boolChange;
        
        
        Math.rand2(1, maxPitchDifference $ int) => int randomDifference;
        
        if(boolChange == 1){
        // change pitch!
            
            // next pitch = 0...7
            if(changePlus > randomInt && randomInt > changeMinus)
                return (lastPitch + randomDifference )% 8;
            else{
                if(lastPitch - randomDifference >= 0) return (lastPitch - randomDifference );
                else return ( 7 + (lastPitch - randomDifference) );
            }
        }
        else{
        // dont change pitch!
            return lastPitch;
        }
    }
    /* 1D RANDOM WALK
    */


/* 
 * Automata and other crazy stuff
 */




/* Utils
 *
 */

fun void setInstrumentValues(string instrumentName){
    float values [0];
    
    getFloatFromCond(weatherCondition) => values["weatherCondition"];
    temperature => values["temperature"];
    humidity => values["humidity"];
    windSpeed => values["windSpeed"];
    getFloatFromDir(windDirection) => values["windDirection"];
    
    instruments[instrumentName].setValues(values);
    
    if(instrumentName == "klavier" || instrumentName == "shakers" || instrumentName == "saxofony")
        [32, 32, 32] @=> instruments[instrumentName].lastNotes;
}

// convert wind direction into float
fun float getFloatFromDir(string direction){
    if(direction == "N")
        return 1.0;
    else if(direction == "NE")
        return 2.0;
    else if(direction == "E")
        return 3.0;
    else if(direction == "SE")
        return 4.0;
    else if(direction == "S")
        return 5.0;
    else if(direction == "SW")
        return 6.0;
    else if(direction == "W")
        return 7.0;
    else if(direction == "NW")
        return 8.0;
    else
        return 0.0;  
}

// convert icon-name into float
fun float getFloatFromCond(string condition){
    ["sunny", "mostly_sunny", "partly_cloudy", "icy"] @=> string classOne[];
    ["chance_of_rain", "mostly_cloudy", "cloudy", "sleet", "snow", "dust", "fog", "smoke", "haze", "flurries"] @=> string classTwo[];
    ["chance_of_storm", "showers", "rain", "chance_of_snow", "mist", "storm", "thunderstorm", "chance_of_tstorm"] @=> string classThree[];
    
    // 1.0: "sunny", "mostly_sunny", "partly_cloudy", "icy" 
    if(isInClass(condition, classOne) == 1)
        return 1.0;
    // 2.0: "chance_of_rain", "mostly_cloudy", "cloudy", "sleet", "snow", "dust", "fog", "smoke", "haze", "flurries"
    else if(isInClass(condition, classTwo) == 1)
        return 2.0;
    
    // 3.0: "chance_of_storm", "showers", "rain", "chance_of_snow", "mist", "storm", "thunderstorm", "chance_of_tstorm" 
    else if(isInClass(condition, classThree) == 1)
        return 3.0;
    
    else
        return -1.0;
}

// returns 1 if condition is in the class, else 0
fun int isInClass(string condition, string classArray[]){

    for(0 => int i; i < classArray.cap(); i++){
        if(condition == classArray[i]){
            return 1; //condition found, return 1
        }
    }
    return 0; //condition not found, return 0
}

fun float getWindSpeedClass(float speed){
    if(speed < 2)
        return 1.0;
    else if(speed <= 10)
        return 2.0;
    else if(speed <= 20)
        return 3.0;
    else
        return 4.0;
}

fun float getHumidityClass(float humidity){
    if(humidity < 30)
        return 5.0;
    else if(humidity < 50)
        return 4.0;
    else if(humidity <= 75)
        return 3.0;
    else if(humidity <= 85)
        return 2.0;
    else
        return 1.0;
}

fun int getOctave(int temperature){
    if(temperature >= 40)
        return 72;
    else if(temperature >= 35)
        return 60;
    else if(temperature >= 27)
        return 48;
    else if(temperature >= 15)
        return 36;
    else if(temperature >= 5)
        return 24;
    else if(temperature >= -5)
        return 12;
    else
        return 0;

}

fun dur getdurationFromInt(int zahl) {
    if (zahl==1)       return takt;
    else if (zahl==2)  return halbTakt;
    else if (zahl==4)  return viertelTakt;
    else if (zahl==8)  return achtelTakt;
    else if (zahl==16) return sechzehntelTakt;
    else if (zahl==32) return zweiunddreissigstelTakt;
    else if (zahl==64) return vierundsechzigstelTakt;
    
}

fun void printReceivedValues(){
    <<<"instrumentToPlay: ",instrumentToPlay>>>;
    <<<"weatherCondition: ",weatherCondition>>>;
    <<<"temperature: ",temperature>>>;
    <<<"humidity: ",humidity>>>;
    <<<"windSpeed: ",windSpeed>>>;
    <<<"windDirection: ",windDirection>>>;
    <<<"------------------------">>>;
}

fun void saveNote(int note, int array[]){
    array[1] => array[0];
    array[2] => array[1];
    note => array[2];
}

// plays a sound with SndBuf
fun void playSound(string file) {
    SndBuf buf => dac;
    "sounds/"+file+".wav" => buf.read;
    buf.length() => dur samples;
    0.05 => buf.gain;
    //<<<samples>>>;
    samples=>now;
    buf =< dac;   
}

fun void playSound(string file,float rate) {
    SndBuf buf => dac;
    "sounds/"+file+".wav" => buf.read;
    0.2 => buf.gain;
    rate => buf.rate;
    //freq => buf.freq;
    //<<<samples>>>;
    buf.samples() => int samples;
    while (buf.pos()<samples) {
        1::samp=>now;
    }
    buf =< dac;   
}

/* 
 * Utils
 */